package kamkor.ann.namerecog.network.converter

import scala.collection.mutable.HashMap

/** Converts human input, ie. "Kowalski" to neural network input.<br><br> 
 * 
 *  In neural network each character is represented by an array of doubles called letter array.
 *  Example. 'a' may be {0,0,0,1}. 'b' may be {0,0,1,0} etc, where conversions are set as follows:<br><br>
 * 
 *  'a' -> 0; 'b' -> 1; 'c' -> 2; 'd' -> 3, where 0, 1, 2 and 3 are indexes of this array. 
 *  When index == 1, then letter[index] == 1, and all other values of this array are 0.<br>    
 *  So each character has its own "bit" set to 1.<br><br>
 * 
 *  Length of letter array is defined by length of the conversionMap. It maps letters
 *  to array indexes. ie. 'a' -> 0, 'b' -> 1 as seen above.<br><br>
 * 
 *  <i>Example:</i><br>  
 *  If conversion map has 2 elements, then human input "aba" will be converted to:  
 *  {0,1,1,0,0,1}, where 'a' -> 0, 'b' -> 1. In other words, one array representing the whole word
 *  "aba" is build from letter arrays: {0,1}, {1,0}, {0,1}<br><br> 
 * 
 * @author kamkor
 * @version 1.0.0
 */
class InputConverter(charToIndexMap: HashMap[Char, Int]) extends IConverter[String, Array[Double]] {
	def this() { this(new HashMap[Char, Int]) }
	
	/**  Removes all elements from conversion map */
	def clearConversionMap() { charToIndexMap.clear() } 
	
	/** Adds char conversion
	 *  
	 * @param kv ('a', 0) will mean a -> 0, where 0 is an index of array element which value is 1.0
	 */
	def addConversion(kv: (Char, Int)) { charToIndexMap += kv }	
	
	/** Adds char conversion. 
	 *  Each added char has index automatically assigned, as (last added char index + 1)
	 *  
	 * @param k ie. 'a'
	 */
	def addConversion(k: Char) { charToIndexMap += k -> charToIndexMap.size }
	
	/** Converts character to letter array, ie. 'c' -> 2 then {1,0,0} will be returned if
	 *  there were 3 conversions added.
	 *   
	 * @param c
	 * @return char converted to letter array (array of doubles), if no conversion for c was added
	 * then array with only 0 values is returned
	 */
	def convert(c: Char): Array[Double] = {
		val size = charToIndexMap.size
		val letterArray = new Array[Double](size)
		try {
			val index = charToIndexMap(c)
			// if size of letter array is 5, then maximum correct index is 4. 
			if (index < size)
				letterArray(index) = 1.0
			letterArray
		} catch {
			case ex: Exception => letterArray // return array with zeroes only
		}			
	}
	
	/** Converts human input to neural network input
	 *  For specifics see class description
	 *  	  
	 * @param human input
	 * @return network input
	 */
	def convert(input: String): Array[Double] =	{		
		val convertedLetters = for (c <- input) yield convert(c)
		val networkInput = convertedLetters.flatten
		networkInput.toArray
	}
	
	/**Converts human input to network input and if converted
	 * input is too short, empty input is appended to the end.
	 * If input is too long, it's end is cutted. 
	 * 
	 * @param input
	 * @param requiredLength of output
	 * @return output
	 */
	def convert(input: String, requiredLength: Int): Array[Double] = {
		val conv = convert(input)	
		val neuralInput = 
			if (conv.length < requiredLength) {
				conv ++ new Array[Double](requiredLength - conv.length)					
			} else if (conv.length > requiredLength){
				conv.view(0, requiredLength).toArray
			}	else {
				conv
			}
		neuralInput
	}
}